/*
 * Board.cpp
 *
 *  Created on: 13.10.2010
 *      Author: jilly
 */
#include <iostream>
#include <stdlib.h>
#include <sstream>
#include "../utils.h"
#include "BoardException.h"
#include "Board.h"

void Board::alloc(unsigned int size)
{
	board = (bool**) calloc(size, sizeof(bool*));
	for (unsigned int i = 0; i < size; i++)
	{
		board[i] = (bool*) calloc(size, sizeof(bool));
	}
	boardArrayAllocated = true;
}

void Board::dealloc()
{
	if (boardArrayAllocated)
	{
		for(unsigned int i = 0; i < size; i++) free(board[i]);
		free(board);
	}
}

Board::Board(unsigned int size)
{
	this->size = size;
	this->numberOfPieces = 0;
	queen = NULL;
	bishop = NULL;
	boardArrayAllocated = false;
	alloc(size);
}

Board::~Board()
{
	delete queen;
	delete bishop;
	dealloc();
}

unsigned int Board::getSize()
{
	return size;
}

unsigned int Board::getNumberOfPieces()
{
	return numberOfPieces;
}

bool Board::isPieceOnPosition(unsigned int x, unsigned int y)
{
	return board[x][y];
}

void Board::removePiece(unsigned int x, unsigned int y)
{
	if (board[x][y])
	{
		board[x][y] = false;
		numberOfPieces--;
	}
}

Board* Board::addPiece(unsigned int x, unsigned int y)
{
	if ((queen != NULL) && (queen->getX() == x && queen->getY() == y))
	{
		stringstream ss;
		ss << "Position [" << x << "," << y << "] is occupied by queen. Can't place piece.";
		throw BoardException(ss.str().c_str());
	}

	if ((bishop != NULL) && (bishop->getX() == x && bishop->getY() == y))
	{
		stringstream ss;
		ss << "Position [" << x << "," << y << "] is occupied by bishop. Can't place piece.";
		throw BoardException(ss.str().c_str());
	}

	if (board[x][y])
	{
		stringstream ss;
		ss << "Position [" << x << "," << y << "] is occupied by another piece. Can't place piece.";
		throw BoardException(ss.str().c_str());
	}
	board[x][y] = true;
	numberOfPieces++;
	return this;
}

Piece* Board::getQueen()
{
	return queen;
}

Piece* Board::getBishop()
{
	return bishop;
}

list<Move*> Board::getQueenPossibleMoves()
{
	list<Move*> moves;
	unsigned int diff;
	unsigned int queenX = queen->getX();
	unsigned int queenY = queen->getY();
	unsigned int bishopX = bishop->getX();
	unsigned int bishopY = bishop->getY();

	//right
	for (unsigned int x=queenX + 1; x < size; x++)
	{
		//can't conquer bishop
		if (bishopY == queenY && x == bishopX) break;
		if (isPieceOnPosition(x, queenY))
		{
			moves.push_back(new Move(MOVE_QUEEN, queenX, queenY, x, queenY));
			break;
		}
		moves.push_front(new Move(MOVE_QUEEN, queenX, queenY, x, queenY));
	}

	//down
	for (unsigned int y=queenY + 1; y < size; y++)
	{
		if (bishopX == queenX && y == bishopY) break;
		if (isPieceOnPosition(queenX, y))
		{
			moves.push_back(new Move(MOVE_QUEEN, queenX, queenY, queenX, y));
			break;
		}
		moves.push_front(new Move(MOVE_QUEEN, queenX, queenY, queenX, y));
	}

	//left
	diff = 1;
	while (true) //while due to unsigned int comparison
	{
		unsigned int newX = queenX - diff;
		if (diff > queenX) break; //loop break
		if (newX == bishopX && queenY == bishopY) break; //can't conquer bishop
		if (isPieceOnPosition(newX, queenY))
		{
			moves.push_back(new Move(MOVE_QUEEN, queenX, queenY, newX, queenY));
			break;
		}
		moves.push_front(new Move(MOVE_QUEEN, queenX, queenY, newX, queenY));
		diff++;
	}

	//up
	diff = 1;
	while (true)
	{
		unsigned int newY = queenY - diff;
		if (diff > queenY) break; //loop break
		if (bishopX == queenX && newY == bishopY) break; //can't conquer bishop
		if (isPieceOnPosition(queenX, newY))
		{
			moves.push_back(new Move(MOVE_QUEEN, queenX, queenY, queenX, newY));
			break; //can't go further because there's piece in way
		}
		moves.push_front(new Move(MOVE_QUEEN, queenX, queenY, queenX, newY));
		diff++;
	}

	//down-right
	diff = 1;
	while (true)
	{
		unsigned int newX = queenX + diff;
		unsigned int newY = queenY + diff;
		if (newX >= size || newY >= size) break;
		if (bishopX == newX && bishopY == newY) break;
		if (isPieceOnPosition(newX, newY))
		{
			moves.push_back(new Move(MOVE_QUEEN, queenX, queenY, newX, newY));
			break;
		}
		moves.push_front(new Move(MOVE_QUEEN, queenX, queenY, newX, newY));
		diff++;
	}

	//up-right
	diff = 1;
	while (true)
	{
		unsigned int newX = queenX + diff;
		unsigned int newY = queenY - diff;
		if (newX >= size || diff > queenY) break;
		if (bishopX == newX && bishopY == newY) break;
		if (isPieceOnPosition(newX, newY))
		{
			moves.push_back(new Move(MOVE_QUEEN, queenX, queenY, newX, newY));
			break;
		}
		moves.push_front(new Move(MOVE_QUEEN, queenX, queenY, newX, newY));
		diff++;
	}

	//down-left
	diff = 1;
	while (true)
	{
		unsigned int newX = queenX - diff;
		unsigned int newY = queenY + diff;
		if (diff > queenX || newY >= size) break;
		if (bishopX == newX && bishopY == newY) break;
		if (isPieceOnPosition(newX, newY))
		{
			moves.push_back(new Move(MOVE_QUEEN, queenX, queenY, newX, newY));
			break;
		}
		moves.push_front(new Move(MOVE_QUEEN, queenX, queenY, newX, newY));
		diff++;
	}

	//up-left
	diff = 1;
	while (true)
	{
		unsigned int newX = queenX - diff;
		unsigned int newY = queenY - diff;
		if (diff > queenX || diff > queenY) break;
		if (bishopX == newX && bishopY == newY) break;
		if (isPieceOnPosition(newX, newY))
		{
			moves.push_back(new Move(MOVE_QUEEN, queenX, queenY, newX, newY));
			break;
		}
		moves.push_front(new Move(MOVE_QUEEN, queenX, queenY, newX, newY));
		diff++;
	}

	return moves;
}

list<Move*> Board::getBishopPossibleMoves()
{
	list<Move*> moves;
	unsigned int queenX = queen->getX();
	unsigned int queenY = queen->getY();
	unsigned int bishopX = bishop->getX();
	unsigned int bishopY = bishop->getY();

	unsigned int diff;
	//down-right
	diff = 1;
	while (true)
	{
		unsigned int newX = bishopX + diff;
		unsigned int newY = bishopY + diff;
		if (newX >= size || newY >= size) break;
		if (queenX == newX && queenY == newY) break;
		if (isPieceOnPosition(newX, newY))
		{
			moves.push_back(new Move(MOVE_BISHOP, bishopX, bishopY, newX, newY));
			break;
		}
		moves.push_front(new Move(MOVE_BISHOP, bishopX, bishopY, newX, newY));
		diff++;
	}

	//up-right
	diff = 1;
	while (true)
	{
		unsigned int newX = bishopX + diff;
		unsigned int newY = bishopY - diff;
		if (newX >= size || diff > bishopY) break;
		if (queenX == newX && queenY == newY) break;
		if (isPieceOnPosition(newX, newY))
		{
			moves.push_back(new Move(MOVE_BISHOP, bishopX, bishopY, newX, newY));
			break;
		}
		moves.push_front(new Move(MOVE_BISHOP, bishopX, bishopY, newX, newY));
		diff++;
	}

	//down-left
	diff = 1;
	while (true)
	{
		unsigned int newX = bishopX - diff;
		unsigned int newY = bishopY + diff;
		if (diff > bishopX || newY >= size) break;
		if (queenX == newX && queenY == newY) break;
		if (isPieceOnPosition(newX, newY))
		{
			moves.push_back(new Move(MOVE_BISHOP, bishopX, bishopY, newX, newY));
			break;
		}
		moves.push_front(new Move(MOVE_BISHOP, bishopX, bishopY, newX, newY));
		diff++;
	}

	//up-left
	diff = 1;
	while (true)
	{
		unsigned int newX = bishopX - diff;
		unsigned int newY = bishopY - diff;
		if (diff > bishopX || diff > bishopY) break;
		if (queenX == newX && queenY == newY) break;
		if (isPieceOnPosition(newX, newY))
		{
			moves.push_back(new Move(MOVE_BISHOP, bishopX, bishopY, newX, newY));
			break;
		}
		moves.push_front(new Move(MOVE_BISHOP, bishopX, bishopY, newX, newY));
		diff++;
	}

	return moves;
}

void Board::placeQueen(unsigned int x, unsigned int y)
{
	if (queen != NULL) throw BoardException("Queen is already on the board");
	if (x >= size || y >= size)
	{
		stringstream ss;
		ss << "Queen's coordinates [" << x << "," << y << "] are out of range";
		throw BoardException(ss.str().c_str());
	}
	if (isPieceOnPosition(x,y))
	{
		stringstream ss;
		ss << "Position [" << x << "," << y << "] is occupied by piece. Can't place queen here.";
		throw BoardException(ss.str().c_str());
	}
	if (bishop != NULL && bishop->getX() == x && bishop->getY() == y)
	{
		stringstream ss;
		ss << "Position [" << x << "," << y << "] is occupied by bishop. Can't place queen here.";
		throw BoardException(ss.str().c_str());
	}
	queen = new Piece(x,y);
}

void Board::placeBishop(unsigned int x, unsigned int y)
{
	if (bishop != NULL) throw BoardException("Bishop is already on the board");
	if (x >= size || y >= size)
	{
		stringstream ss;
		ss << "Bishop's coordinates [" << x << "," << y << "] are out of range";
		throw BoardException(ss.str().c_str());
	}
	if (isPieceOnPosition(x,y))
	{
		stringstream ss;
		ss << "Position [" << x << "," << y << "] is occupied by piece. Can't place bishop here.";
		throw BoardException(ss.str().c_str());
	}
	if (queen != NULL && queen->getX() == x && queen->getY() == y)
	{
		stringstream ss;
		ss << "Position [" << x << "," << y << "] is occupied by bishop. Can't place queen here.";
		throw BoardException(ss.str().c_str());
	}
	bishop = new Piece(x,y);
}

bool Board::moveQueen(unsigned int x, unsigned int y)
{
	//cout << "Q: [" << queen->getX() << "," << queen->getY() << "] -> [" << x << "," << y <<"]" << endl;
	bool hit;
	if (isPieceOnPosition(x, y))
	{
		removePiece(x,y);
		hit = true;
	}
	else
	{
		hit = false;
	}
	queen->moveTo(x,y);
	return hit;
}

bool Board::moveBishop(unsigned int x, unsigned int y)
{
	//cout << "B: [" << bishop->getX() << "," << bishop->getY() << "] -> [" << x << "," << y <<"]" << endl;
	bool hit;
	if (isPieceOnPosition(x,y))
	{
		removePiece(x,y);
		hit = true;
	}
	else
	{
		hit = false;
	}
	bishop->moveTo(x,y);
	return hit;
}

bool** Board::getBoard()
{
	return board;
}

void Board::writeBoard(ofstream& stream)
{
	stream << " ";
	int totalDigits = digits(size - 1) + 1;
	for (unsigned int x=0; x < size; x++)
	{
		int numberDigits = digits(x);
		stream << x;
		if (x < (size - 1)) writeSpaces(totalDigits - numberDigits, stream);
	}
	stream << endl;

	for (unsigned int y=0; y < size; y++)
	{
		stream << y;
		for (unsigned int x=0; x < size; x++)
		{
			if (isPieceOnPosition(x,y))
			{
				stream << "+";
			}
			else if (queen != NULL && queen->getX() == x && queen->getY() == y)
			{
				stream << "Q";
			}
			else if (bishop != NULL && bishop->getX() == x && bishop->getY() == y)
			{
				stream << "B";
			}
			else
			{
				stream << ".";
			}
			if (x < (size - 1)) writeSpaces(totalDigits - 1, stream);
		}
		stream << endl;
	}
	stream << endl;
}

void Board::printBoard()
{
	cout << " ";
	int totalDigits = digits(size - 1) + 1;
	for (unsigned int x=0; x < size; x++)
	{
		int numberDigits = digits(x);
		cout << x;
		if (x < (size - 1)) printSpaces(totalDigits - numberDigits);
	}
	cout << endl;

	for (unsigned int y=0; y < size; y++)
	{
		cout << y;
		for (unsigned int x=0; x < size; x++)
		{
			if (isPieceOnPosition(x,y))
			{
				cout << "+";
			}
			else if (queen != NULL && queen->getX() == x && queen->getY() == y)
			{
				cout << "Q";
			}
			else if (bishop != NULL && bishop->getX() == x && bishop->getY() == y)
			{
				cout << "B";
			}
			else
			{
				cout << ".";
			}
			if (x < (size - 1)) printSpaces(totalDigits - 1);
		}
		cout << endl;
	}
	cout << endl;
}
